/****************************************************************************
 *
 * Copyright 2016 Samsung Electronics All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 ****************************************************************************/
/****************************************************************************
 * drivers/usbdev/usbmsc.h
 *
 *   Copyright (C) 2008-2013, 2015 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Mass storage class device.  Bulk-only with SCSI subclass.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#ifndef __DRIVERS_USBDEV_USBMSC_H
#define __DRIVERS_USBDEV_USBMSC_H

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <tinyara/config.h>

#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <queue.h>
#include <semaphore.h>

#include <tinyara/fs/fs.h>
#include <tinyara/usb/storage.h>
#include <tinyara/usb/usbdev.h>

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/
/* Configuration ************************************************************/
/* If the USB mass storage device is configured as part of a composite device
 * then both CONFIG_USBDEV_COMPOSITE and CONFIG_USBMSC_COMPOSITE must be
 * defined.
 */

#ifndef CONFIG_USBDEV_COMPOSITE
#undef CONFIG_USBMSC_COMPOSITE
#endif

#if defined(CONFIG_USBMSC_COMPOSITE) && !defined(CONFIG_USBMSC_STRBASE)
#define CONFIG_USBMSC_STRBASE (4)
#endif

/* Interface IDs.  If the mass storage driver is built as a component of a
 * composite device, then the interface IDs may need to be offset.
 */

#ifndef CONFIG_USBMSC_COMPOSITE
#undef CONFIG_USBMSC_IFNOBASE
#define CONFIG_USBMSC_IFNOBASE 0
#endif

#ifndef CONFIG_USBMSC_IFNOBASE
#define CONFIG_USBMSC_IFNOBASE 0
#endif

/* Number of requests in the write queue */

#ifndef CONFIG_USBMSC_NWRREQS
#define CONFIG_USBMSC_NWRREQS 4
#endif

/* Number of requests in the read queue */

#ifndef CONFIG_USBMSC_NRDREQS
#define CONFIG_USBMSC_NRDREQS 4
#endif

/* Logical endpoint numbers / max packet sizes */

#ifndef CONFIG_USBMSC_EPBULKOUT
#warning "EPBULKOUT not defined in the configuration"
#define CONFIG_USBMSC_EPBULKOUT 2
#endif

#ifndef CONFIG_USBMSC_EPBULKIN
#warning "EPBULKIN not defined in the configuration"
#define CONFIG_USBMSC_EPBULKIN 3
#endif

/* Packet and request buffer sizes */

#ifndef CONFIG_USBMSC_COMPOSITE
#ifndef CONFIG_USBMSC_EP0MAXPACKET
#define CONFIG_USBMSC_EP0MAXPACKET 64
#endif
#endif

#ifndef CONFIG_USBMSC_BULKINREQLEN
#ifdef CONFIG_USBDEV_DUALSPEED
#define CONFIG_USBMSC_BULKINREQLEN 512
#else
#define CONFIG_USBMSC_BULKINREQLEN 64
#endif
#else
#ifdef CONFIG_USBDEV_DUALSPEED
#if CONFIG_USBMSC_BULKINREQLEN < 512
#warning "Bulk in buffer size smaller than max packet"
#undef CONFIG_USBMSC_BULKINREQLEN
#define CONFIG_USBMSC_BULKINREQLEN 512
#endif
#else
#if CONFIG_USBMSC_BULKINREQLEN < 64
#warning "Bulk in buffer size smaller than max packet"
#undef CONFIG_USBMSC_BULKINREQLEN
#define CONFIG_USBMSC_BULKINREQLEN 64
#endif
#endif
#endif

#ifndef CONFIG_USBMSC_BULKOUTREQLEN
#ifdef CONFIG_USBDEV_DUALSPEED
#define CONFIG_USBMSC_BULKOUTREQLEN 512
#else
#define CONFIG_USBMSC_BULKOUTREQLEN 64
#endif
#else
#ifdef CONFIG_USBDEV_DUALSPEED
#if CONFIG_USBMSC_BULKOUTREQLEN < 512
#warning "Bulk in buffer size smaller than max packet"
#undef CONFIG_USBMSC_BULKOUTREQLEN
#define CONFIG_USBMSC_BULKOUTREQLEN 512
#endif
#else
#if CONFIG_USBMSC_BULKOUTREQLEN < 64
#warning "Bulk in buffer size smaller than max packet"
#undef CONFIG_USBMSC_BULKOUTREQLEN
#define CONFIG_USBMSC_BULKOUTREQLEN 64
#endif
#endif
#endif

/* Vendor and product IDs and strings */

#ifndef CONFIG_USBMSC_COMPOSITE
#ifndef CONFIG_USBMSC_VENDORID
#warning "CONFIG_USBMSC_VENDORID not defined"
#define CONFIG_USBMSC_VENDORID 0x584e
#endif

#ifndef CONFIG_USBMSC_PRODUCTID
#warning "CONFIG_USBMSC_PRODUCTID not defined"
#define CONFIG_USBMSC_PRODUCTID 0x5342
#endif

#ifndef CONFIG_USBMSC_VERSIONNO
#define CONFIG_USBMSC_VERSIONNO (0x0399)
#endif

#ifndef CONFIG_USBMSC_VENDORSTR
#warning "No Vendor string specified"
#define CONFIG_USBMSC_VENDORSTR  "NuttX"
#endif

#ifndef CONFIG_USBMSC_PRODUCTSTR
#warning "No Product string specified"
#define CONFIG_USBMSC_PRODUCTSTR "USBdev Storage"
#endif

#undef CONFIG_USBMSC_SERIALSTR
#define CONFIG_USBMSC_SERIALSTR "0101"
#endif

#undef CONFIG_USBMSC_CONFIGSTR
#define CONFIG_USBMSC_CONFIGSTR "Bulk"

/* SCSI daemon */

#ifndef CONFIG_USBMSC_SCSI_PRIO
#define CONFIG_USBMSC_SCSI_PRIO 128
#endif

#ifndef CONFIG_USBMSC_SCSI_STACKSIZE
#define CONFIG_USBMSC_SCSI_STACKSIZE 2048
#endif

/* Packet and request buffer sizes */

#ifndef CONFIG_USBMSC_EP0MAXPACKET
#define CONFIG_USBMSC_EP0MAXPACKET 64
#endif

/* USB Controller */

#ifdef CONFIG_USBDEV_SELFPOWERED
#define USBMSC_SELFPOWERED USB_CONFIG_ATTR_SELFPOWER
#else
#define USBMSC_SELFPOWERED (0)
#endif

#ifdef  CONFIG_USBDEV_REMOTEWAKEUP
#define USBMSC_REMOTEWAKEUP USB_CONFIG_ATTR_WAKEUP
#else
#define USBMSC_REMOTEWAKEUP (0)
#endif

#ifndef CONFIG_USBDEV_MAXPOWER
#define CONFIG_USBDEV_MAXPOWER 100
#endif

/* Current state of the worker thread */

#define USBMSC_STATE_NOTSTARTED       (0)	/* Thread has not yet been started */
#define USBMSC_STATE_STARTED          (1)	/* Started, but is not yet initialized */
#define USBMSC_STATE_IDLE             (2)	/* Started and waiting for commands */
#define USBMSC_STATE_CMDPARSE         (3)	/* Processing a received command */
#define USBMSC_STATE_CMDREAD          (4)	/* Processing a SCSI read command */
#define USBMSC_STATE_CMDWRITE         (5)	/* Processing a SCSI write command */
#define USBMSC_STATE_CMDFINISH        (6)	/* Finish command processing */
#define USBMSC_STATE_CMDSTATUS        (7)	/* Processing the final status of the command */
#define USBMSC_STATE_TERMINATED       (8)	/* Thread has exitted */

/* Event communicated to worker thread */

#define USBMSC_EVENT_NOEVENTS         (0)	/* There are no outstanding events */
#define USBMSC_EVENT_READY            (1 << 0)	/* Initialization is complete */
#define USBMSC_EVENT_RDCOMPLETE       (1 << 1)	/* A read has completed there is data to be processed */
#define USBMSC_EVENT_WRCOMPLETE       (1 << 2)	/* A write has completed and a request is available */
#define USBMSC_EVENT_TERMINATEREQUEST (1 << 3)	/* Shutdown requested */
#define USBMSC_EVENT_DISCONNECT       (1 << 4)	/* USB disconnect received */
#define USBMSC_EVENT_RESET            (1 << 5)	/* USB storage setup reset received */
#define USBMSC_EVENT_CFGCHANGE        (1 << 6)	/* USB setup configuration change received */
#define USBMSC_EVENT_IFCHANGE         (1 << 7)	/* USB setup interface change received */
#define USBMSC_EVENT_ABORTBULKOUT     (1 << 8)	/* SCSI receive failure */

/* SCSI command flags (passed to usbmsc_setupcmd()) */

#define USBMSC_FLAGS_DIRMASK          (0x03)	/* Bits 0-1: Data direction */
#define USBMSC_FLAGS_DIRNONE          (0x00)	/*   No data to send */
#define USBMSC_FLAGS_DIRHOST2DEVICE   (0x01)	/*   Host-to-device */
#define USBMSC_FLAGS_DIRDEVICE2HOST   (0x02)	/*   Device-to-host */
#define USBMSC_FLAGS_BLOCKXFR         (0x04)	/* Bit 2: Command is a block transfer request */
#define USBMSC_FLAGS_LUNNOTNEEDED     (0x08)	/* Bit 3: Command does not require a valid LUN */
#define USBMSC_FLAGS_UACOKAY          (0x10)	/* Bit 4: Command OK if unit attention condition */
#define USBMSC_FLAGS_RETAINSENSEDATA  (0x20)	/* Bit 5: Do not clear sense data */

/* Descriptors **************************************************************/

/* Big enough to hold our biggest descriptor */

#define USBMSC_MXDESCLEN              (64)
#define USBMSC_MAXSTRLEN              (USBMSC_MXDESCLEN - 2)

/* String language */

#define USBMSC_STR_LANGUAGE           (0x0409)	/* en-us */

/* Descriptor strings */

#ifndef CONFIG_USBMSC_COMPOSITE
#define USBMSC_MANUFACTURERSTRID    (1)
#define USBMSC_PRODUCTSTRID         (2)
#define USBMSC_SERIALSTRID          (3)
#define USBMSC_CONFIGSTRID          (4)
#define USBMSC_INTERFACESTRID       USBMSC_CONFIGSTRID

#undef CONFIG_USBMSC_STRBASE
#define CONFIG_USBMSC_STRBASE       (0)
#else
#define USBMSC_INTERFACESTRID       (CONFIG_USBMSC_STRBASE + 1)
#endif

#define USBMSC_LASTSTRID              USBMSC_INTERFACESTRID
#define USBMSC_NSTRIDS               (USBMSC_LASTSTRID - CONFIG_USBMSC_STRBASE)

#define USBMSC_NCONFIGS               (1)	/* Number of configurations supported */

/* Configuration Descriptor */

#define USBMSC_NINTERFACES            (1)	/* Number of interfaces in the configuration */
#define USBMSC_INTERFACEID            (CONFIG_USBMSC_IFNOBASE+0)
#define USBMSC_ALTINTERFACEID         (0)

#define USBMSC_CONFIGIDNONE           (0)	/* Config ID means to return to address mode */
#define USBMSC_CONFIGID               (1)	/* The only supported configuration ID */

/* Interface description */

#define USBMSC_NENDPOINTS             (2)	/* Number of endpoints in the interface  */

/* Endpoint configuration */

#define USBMSC_EPOUTBULK_ADDR         (CONFIG_USBMSC_EPBULKOUT)
#define USBMSC_EPOUTBULK_ATTR         (USB_EP_ATTR_XFER_BULK)

#define USBMSC_EPINBULK_ADDR          (USB_DIR_IN|CONFIG_USBMSC_EPBULKIN)
#define USBMSC_EPINBULK_ATTR          (USB_EP_ATTR_XFER_BULK)

#define USBMSC_HSBULKMAXPACKET        (512)
#define USBMSC_HSBULKMXPKTSHIFT       (9)
#define USBMSC_HSBULKMXPKTMASK        (0x000001ff)
#define USBMSC_FSBULKMAXPACKET        (64)
#define USBMSC_FSBULKMXPKTSHIFT       (6)
#define USBMSC_FSBULKMXPKTMASK        (0x0000003f)

/* Macros for dual speed vs. full speed only operation */

#ifdef CONFIG_USBDEV_DUALSPEED
#define USBMSC_EPBULKINDESC(hs)  \
	usbmsc_getepdesc((hs) ? USBMSC_EPHSBULKIN : USBMSC_EPFSBULKIN)
#define USBMSC_EPBULKOUTDESC(hs) \
	usbmsc_getepdesc((hs) ? USBMSC_EPHSBULKOUT : USBMSC_EPFSBULKOUT)
#define USBMSC_BULKMAXPACKET(hs) \
	((hs) ? USBMSC_HSBULKMAXPACKET : USBMSC_FSBULKMAXPACKET)
#define USBMSC_BULKMXPKTSHIFT(d) \
	(((d)->speed == USB_SPEED_HIGH) ? USBMSC_HSBULKMXPKTSHIFT : USBMSC_FSBULKMXPKTSHIFT)
#define USBMSC_BULKMXPKTMASK(d) \
	(((d)->speed == USB_SPEED_HIGH) ? USBMSC_HSBULKMXPKTMASK : USBMSC_FSBULKMXPKTMASK)
#else
#define USBMSC_EPBULKINDESC(d)    usbmsc_getepdesc(USBMSC_EPFSBULKIN)
#define USBMSC_EPBULKOUTDESC(d)   usbmsc_getepdesc(USBMSC_EPFSBULKOUT)
#define USBMSC_BULKMAXPACKET(hs)  USBMSC_FSBULKMAXPACKET
#define USBMSC_BULKMXPKTSHIFT(d)  USBMSC_FSBULKMXPKTSHIFT
#define USBMSC_BULKMXPKTMASK(d)   USBMSC_FSBULKMXPKTMASK
#endif

/* Configuration descriptor size */

#ifndef CONFIG_USBMSC_COMPOSITE

/* Number of individual descriptors in the configuration descriptor:
 * (1) Configuration descriptor + (1) interface descriptor + (2) interface
 * descriptors.
 */

#define USBMSC_CFGGROUP_SIZE      (4)

/* The size of the config descriptor: (9 + 9 + 2*7) = 32 */

#define SIZEOF_USBMSC_CFGDESC \
	(USB_SIZEOF_CFGDESC + USB_SIZEOF_IFDESC + USBMSC_NENDPOINTS * USB_SIZEOF_EPDESC)

#else

/* Number of individual descriptors in the configuration descriptor:
 * (1) interface descriptor + (2) interface descriptors.
 */

#define USBMSC_CFGGROUP_SIZE     (3)

/* The size of the config descriptor: (9 + 2 * 7) = 23 */

#define SIZEOF_USBMSC_CFGDESC \
	(USB_SIZEOF_IFDESC + USBMSC_NENDPOINTS * USB_SIZEOF_EPDESC)

#endif

/* Block driver helpers *****************************************************/

#define USBMSC_DRVR_READ(l, b, s, n) ((l)->inode->u.i_bops->read((l)->inode, b, s, n))
#define USBMSC_DRVR_WRITE(l, b, s, n) ((l)->inode->u.i_bops->write((l)->inode, b, s, n))
#define USBMSC_DRVR_GEOMETRY(l, g) ((l)->inode->u.i_bops->geometry((l)->inode, g))

/* Everpresent MIN/MAX macros ***********************************************/

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif

/****************************************************************************
 * Public Types
 ****************************************************************************/
/* Endpoint descriptors */

enum usbmsc_epdesc_e {
	USBMSC_EPFSBULKOUT = 0,		/* Full speed bulk OUT endpoint descriptor */
	USBMSC_EPFSBULKIN			/* Full speed bulk IN endpoint descriptor */
#ifdef CONFIG_USBDEV_DUALSPEED
	,
	USBMSC_EPHSBULKOUT,			/* High speed bulk OUT endpoint descriptor */
	USBMSC_EPHSBULKIN			/* High speed bulk IN endpoint descriptor */
#endif
};

/* Container to support a list of requests */

struct usbmsc_req_s {
	FAR struct usbmsc_req_s *flink;	/* Implements a singly linked list */
	FAR struct usbdev_req_s *req;	/* The contained request */
};

/* This structure describes one LUN: */

struct usbmsc_lun_s {
	struct inode
		*inode;						/* Inode structure of open'ed block driver */
	uint8_t readonly:1;			/* Media is read-only */
	uint8_t locked:1;			/* Media removal is prevented */
	uint16_t sectorsize;		/* The size of one sector */
	uint32_t sd;				/* Sense data */
	uint32_t sdinfo;			/* Sense data information */
	uint32_t uad;				/* Unit needs attention data */
	off_t startsector;			/* Sector offset to start of partition */
	size_t nsectors;			/* Number of sectors in the partition */
};

/* Describes the overall state of one instance of the driver */

struct usbmsc_dev_s {
	FAR struct usbdev_s
		*usbdev;					/* usbdev driver pointer (Non-null if registered) */

	/* SCSI worker kernel thread interface */

	pid_t thpid;				/* The worker thread task ID */
	sem_t thsynch;				/* Used to synchronizer terminal events */
	sem_t thlock;				/* Used to get exclusive access to the state data */
	sem_t thwaitsem;			/* Used to signal worker thread */
	volatile bool thwaiting;	/* True: worker thread is waiting for an event */
	volatile uint8_t thstate;	/* State of the worker thread */
	volatile uint16_t theventset;	/* Set of pending events signaled to worker thread */
	volatile uint8_t thvalue;	/* Value passed with the event (must persist) */

	/* Storage class configuration and state */

	uint8_t nluns:4;			/* Number of LUNs */
	uint8_t config;				/* Configuration number */

	/* Endpoints */

	FAR struct usbdev_ep_s *epbulkin;	/* Bulk IN endpoint structure */
	FAR struct usbdev_ep_s *epbulkout;	/* Bulk OUT endpoint structure */
	FAR struct usbdev_req_s
		*ctrlreq;					/* Control request (for ep0 setup responses) */

	/* SCSI command processing */

	struct usbmsc_lun_s *lun;	/* Currently selected LUN */
	struct usbmsc_lun_s *luntab;	/* Allocated table of all LUNs */
	uint8_t cdb[USBMSC_MAXCDBLEN];	/* Command data (cdb[]) from CBW */
	uint8_t phaseerror:1;		/* Need to send phase sensing status */
	uint8_t shortpacket:1;		/* Host transmission stopped unexpectedly */
	uint8_t cbwdir:2;			/* Direction from CBW. See USBMSC_FLAGS_DIR* definitions */
	uint8_t cdblen;				/* Length of cdb[] from CBW */
	uint8_t cbwlun;				/* LUN from the CBW */
	uint16_t nsectbytes;		/* Bytes buffered in iobuffer[] */
	uint16_t nreqbytes;			/* Bytes buffered in head write requests */
	uint16_t iosize;			/* Size of iobuffer[] */
	uint32_t cbwlen;			/* Length of data from CBW */
	uint32_t cbwtag;			/* Tag from the CBW */
	union {
		uint32_t xfrlen;		/* Read/Write: Sectors remaining to be transferred */
		uint32_t alloclen;		/* Other device-to-host: Host allocation length */
	} u;
	uint32_t sector;			/* Current sector (relative to lun->startsector) */
	uint32_t residue;			/* Untransferred amount reported in the CSW */
	uint8_t *iobuffer;			/* Buffer for data transfers */

	/* Write request list */

	struct sq_queue_s wrreqlist;	/* List of empty write request containers */
	struct sq_queue_s rdreqlist;	/* List of filled read request containers */

	/* Pre-allocated write request containers.  The write requests will
	 * be linked in a free list (wrreqlist), and used to send requests to
	 * EPBULKIN; Read requests will be queued in the EBULKOUT.
	 */

	struct usbmsc_req_s wrreqs[CONFIG_USBMSC_NWRREQS];
	struct usbmsc_req_s rdreqs[CONFIG_USBMSC_NRDREQS];
};

/****************************************************************************
 * Public Data
 ****************************************************************************/

#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C" {
#else
#define EXTERN extern
#endif

/* String *******************************************************************/

/* Mass storage class vendor/product/serial number strings */

#ifndef CONFIG_USBMSC_COMPOSITE
EXTERN const char g_mscvendorstr[];
EXTERN const char g_mscproductstr[];
EXTERN const char g_mscserialstr[];

/* If we are using a composite device, then vendor/product/serial number strings
 * are provided by the composite device logic.
 */

#else
EXTERN const char g_compvendorstr[];
EXTERN const char g_compproductstr[];
EXTERN const char g_compserialstr[];

#define g_mscvendorstr  g_compvendorstr
#define g_mscproductstr g_compproductstr
#define g_mscserialstr  g_compserialstr
#endif

/* Used to hand-off the state structure when the SCSI worker thread is started */

EXTERN FAR struct usbmsc_dev_s *g_usbmsc_handoff;

/************************************************************************************
 * Public Function Prototypes
 ************************************************************************************/

/************************************************************************************
 * Name: usbmsc_scsi_lock
 *
 * Description:
 *   Get exclusive access to SCSI state data.
 *
 ****************************************************************************/

void usbmsc_scsi_lock(FAR struct usbmsc_dev_s *priv);

/************************************************************************************
 * Name: usbmsc_scsi_unlock
 *
 * Description:
 *   Relinquish exclusive access to SCSI state data.
 *
 ************************************************************************************/

#define usbmsc_scsi_unlock(priv) sem_post(&priv->thlock)

/************************************************************************************
 * Name: usbmsc_scsi_signal
 *
 * Description:
 *   Signal the SCSI worker thread that SCSI events need service.
 *
 ************************************************************************************/

void usbmsc_scsi_signal(FAR struct usbmsc_dev_s *priv);

/************************************************************************************
 * Name: usbmsc_synch_signal
 *
 * Description:
 *   ACK controlling tasks request for synchronization.
 *
 ************************************************************************************/

#define usbmsc_synch_signal(priv) sem_post(&priv->thsynch)

/************************************************************************************
 * Name: usbmsc_mkstrdesc
 *
 * Description:
 *   Construct a string descriptor
 *
 ************************************************************************************/

struct usb_strdesc_s;
int usbmsc_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc);

/************************************************************************************
 * Name: usbmsc_getepdesc
 *
 * Description:
 *   Return a pointer to the raw device descriptor
 *
 ************************************************************************************/

#ifndef CONFIG_USBMSC_COMPOSITE
FAR const struct usb_devdesc_s *usbmsc_getdevdesc(void);
#endif

/************************************************************************************
 * Name: usbmsc_getepdesc
 *
 * Description:
 *   Return a pointer to the raw endpoint descriptor (used for configuring endpoints)
 *
 ************************************************************************************/

struct usb_epdesc_s;
FAR const struct usb_epdesc_s *usbmsc_getepdesc(enum usbmsc_epdesc_e epid);

/************************************************************************************
 * Name: usbmsc_mkcfgdesc
 *
 * Description:
 *   Construct the configuration descriptor
 *
 ************************************************************************************/

#ifdef CONFIG_USBDEV_DUALSPEED
int16_t usbmsc_mkcfgdesc(FAR uint8_t *buf, uint8_t speed, uint8_t type);
#else
int16_t usbmsc_mkcfgdesc(FAR uint8_t *buf);
#endif

/************************************************************************************
 * Name: usbmsc_getqualdesc
 *
 * Description:
 *   Return a pointer to the raw qual descriptor
 *
 ************************************************************************************/

#if !defined(CONFIG_USBMSC_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED)
FAR const struct usb_qualdesc_s *usbmsc_getqualdesc(void);
#endif

/****************************************************************************
 * Name: usbmsc_scsi_main
 *
 * Description:
 *   This is the main function of the USB storage worker thread.  It loops
 *   until USB-related events occur, then processes those events accordingly
 *
 ****************************************************************************/

int usbmsc_scsi_main(int argc, char *argv[]);

/****************************************************************************
 * Name: usbmsc_setconfig
 *
 * Description:
 *   Set the device configuration by allocating and configuring endpoints and
 *   by allocating and queue read and write requests.
 *
 ****************************************************************************/

int usbmsc_setconfig(FAR struct usbmsc_dev_s *priv, uint8_t config);

/****************************************************************************
 * Name: usbmsc_resetconfig
 *
 * Description:
 *   Mark the device as not configured and disable all endpoints.
 *
 ****************************************************************************/

void usbmsc_resetconfig(FAR struct usbmsc_dev_s *priv);

/****************************************************************************
 * Name: usbmsc_wrcomplete
 *
 * Description:
 *   Handle completion of write request.  This function probably executes
 *   in the context of an interrupt handler.
 *
 ****************************************************************************/

void usbmsc_wrcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req);

/****************************************************************************
 * Name: usbmsc_rdcomplete
 *
 * Description:
 *   Handle completion of read request on the bulk OUT endpoint.  This
 *   is handled like the receipt of serial data on the "UART"
 *
 ****************************************************************************/

void usbmsc_rdcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req);

/****************************************************************************
 * Name: usbmsc_deferredresponse
 *
 * Description:
 *   Some EP0 setup request cannot be responded to immediately becuase they
 *   require some asynchronous action from the SCSI worker thread.  This
 *   function is provided for the SCSI thread to make that deferred response.
 *   The specific requests that require this deferred response are:
 *
 *   1. USB_REQ_SETCONFIGURATION,
 *   2. USB_REQ_SETINTERFACE, or
 *   3. USBMSC_REQ_MSRESET
 *
 *   In all cases, the success response is a zero-length packet; the failure
 *   response is an EP0 stall.
 *
 * Input parameters:
 *   priv  - Private state structure for this USB storage instance
 *   stall - true is the action failed and a stall is required
 *
 ****************************************************************************/

void usbmsc_deferredresponse(FAR struct usbmsc_dev_s *priv, bool failed);

#undef EXTERN
#if defined(__cplusplus)
}
#endif

#endif							/* #define __DRIVERS_USBDEV_USBMSC_H */
